// Copyright (C) 2010  Andrew H. Chan
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// The Compute class does most of the computations to calculate the sampling probability.

#ifndef _compute_H__
#define _compute_H__

#include "basic_functions.h"
#include "input_parser.h"
#include "multiM.h"
#include "packed_config.h"

class Compute {
    public:
        // constructor
        Compute(InputParser& data);

        // functions
        int validate_input();
        int run();
        int init_probs();
        int convert_input_to_config();
        int create_maps();
        int display_samp_config(samp_config_t const& samp_config) const;
        int display_samp_config(packed_config_t const& packed_config) const;
        int display_packed_config(packed_config_t const& packed_config) const;
        int get_seq_config();
        int compute_probs();
        
        // functions called from compute_probs()
        int explore_graph(packed_config_t& packed_config);
        int add_vertex(packed_config_t& source, packed_config_t& target, double coefficient, packed_queue_t& bfs_queue);

        // functions for enumerating the powerset of a set of integers
        int next_subset(std::vector<bool>& mask, int n);
        int next_conf(index_t& conf, int max_allele, int n);

        int next_hap(index_t& index, int max_allele);

        // functions for finding strongly connected components
        // Uses Tarjan's algorithm
        int compute_sccs(vv_packed_config_t& list_of_sccs);
        int scc_explore(packed_config_t const& cur_samp, size_t& scc_index, packed_uint_hash_t& scc_id, packed_uint_hash_t& scc_lowlink, packed_stack_t& scc_stack, packed_int_hash_t& scc_in_stack, vv_packed_config_t& list_of_sccs);

        // functions to topologically sort strongly connected components
        int topological_sort_scc(vv_packed_config_t& list_of_sccs, scc_rank_t& scc_rank);
        int dfs_explore(packed_config_t const& packed_config, uint& post_number, packed_uint_hash_t& post_numbers, packed_int_hash_t& visited);

        // converts scc to system by filling in b_vector and A_matrix
        int convert_scc_to_system(packed_vector_t& scc_to_solve, vector_t& b_vector, matrix_t& A_matrix, packed_uint_hash_t& packed_index_system);

        // Converts a samp config to a packed config 
        int convert_to_packed(samp_config_t const& samp_config, packed_config_t& packed_config) const;
        // Converts a packed config to samp config
        int convert_to_samp(packed_config_t const& packed_config, samp_config_t& samp_config) const;

        // Displays all probabilities in probs
        // Note: This could be a lot of probabilities (every sample configuration encountered
        int display_probs();

        // solves the system Ax=b and puts solution in sol_vector
        int system_solve(vector_t& b_vector, matrix_t& A_matrix, vector_t& sol_vector);

        // On the Mac, it's difficult to use LAPACK for linear algebra operations. It's also difficult to install LAPACK because LAPACK is written in Fortran, and Mac no longer includes a Fortran compiler. It's possible to fix this, but I haven't done so yet (because it works just fine on Linux).
        // This is an iterative linear system solver
        int temp_lin_solve(vector_t& b_vector, matrix_t& A_matrix, vector_t& sol_vector);

        // data members
        InputParser data;
        seq_config_t seq_config;
        probs_type probs; // Hash: samp_config_t -> double (the sampling probabilities)

        ////////////////
        // border_vert
        // hash map: samp_config_t -> double
        // known probabilities * coefficients for the vertices that are right outside the block
        // This should be reset for every samp_config in seq_config (i.e. it should be reset for every block)
        packed_hash_t border_vert;
        ////////////////

        // Adjacency list to store the graph
        adj_list_t graph;

        hap_to_id_t hap_to_id;
        id_to_hap_t id_to_hap;

        // Statistics gathering variables
        size_t num_vert_added;
        size_t num_edge_added;
        size_t num_base_cases;
        size_t coeff_zero;
        size_t roots_already_known;
        size_t prev_seen_vertex;
        size_t num_zero_probs;
        size_t mut_terms;
        size_t num_samp_added_for_root;
        size_t num_edge_added_for_root;
        size_t avg_samp_size_sum;
};

#endif
